import { shallowRef, reactive, toRefs, computed } from 'vue'
import { RouterLink } from './router-link'
import { RouterView } from './router-view'

function normalize(record) {
    return {
        path: record.path,
        name: record.name,
        meta: record.meta || {},
        components: {
            default: record.component
        },
        children: record.children || [],
        beforeEnter: record.beforeEnter, // 路由的配置 
    }
}




function createMatch(record, parent) {
    const matcher = {
        path: record.path,
        record,
        parent,
        children: []
    }
    if (parent) {
        parent.children.push(matcher)
    }
    return matcher
}
function createRouterMatcher(routes) { // 用户登录后才有的权限  addRoute
    const matchers = []
    function addRoute(record, parent) {
        let normalizedRecord = normalize(record);

        if (parent) {
            normalizedRecord.path = parent.path + '/' + normalizedRecord.path
        }
        const matcher = createMatch(normalizedRecord, parent)
        matchers.push(matcher)
        const children = normalizedRecord.children
        for (let i = 0; i < children.length; i++) {
            addRoute(children[i], matcher)
        }
    }
    function resolve(path) {
        let matched = []
        // 例如我们访问的是 /my/a 组件 -》 /my
        let matcher = matchers.find(matcher => matcher.path === path);

        while (matcher) {
            matched.unshift(matcher.record)// /my  /my/a
            matcher = matcher.parent;
        }

        return {
            path,
            matched
        }
    }

    // 递归添加路由
    routes.forEach(route => addRoute(route))
    return {
        addRoute,
        resolve
    }
}


const START_LOCATION = {
    path: '/',
    matched: [],
    // query params  meta ....
}




export function createRouter({ history, routes }) {

    // 路由匹配器： 根据路径进行匹配
    const matcher = createRouterMatcher(routes);
    const currentLocation = shallowRef(START_LOCATION)

    let ready;
    function markReady() {
        if (ready) return;
        ready = true;
        history.listen((to, from) => {
            to = matcher.resolve(to);
            from = currentLocation.value;
            finalNavigation(to, from)
        })
    }
    function finalNavigation(to, from) {
        // 我如何知道 用的事 replace 还是push
        if (from === START_LOCATION) { // 默认就是替换逻辑
            history.replace(to.path)
        } else {
            history.push(to.path)
        }
        currentLocation.value = to; // 更细路径， 更新
        markReady();
    }


    function extractRecords(to, from) {
        const leavingRecords = []
        const updatingRecords = []
        const enteringRecords = []

        let len = Math.max(to.matched.length, from.matched.length); // 获取那个匹配的多，以哪一个为进准
        for (let i = 0; i < len; i++){
            const fromRecord = from.matched[i];
            if (fromRecord) {
                if (to.matched.find(record => record.path == fromRecord.path)) {
                    updatingRecords.push(fromRecord)
                } else {
                    leavingRecords.push(fromRecord)
                }
            }
            const toRecord = to.matched[i]
            if (toRecord) {
                if (!from.matched.find(record => record.path === toRecord.path)) {
                    enteringRecords.push(toRecord)
                }
            }
        }
        return [
            leavingRecords,updatingRecords,enteringRecords
        ]
        
    }

    // my -> beforeRouteleave  my/a -> beforeRouteleanve
    function guardToPromise(guard,to,from,record) {
        return () => new Promise((resolve, reject) => { // 返回函数的目的是为了可以组合多个guard
            const next = resolve
            const r = guard.call(record, to, from, next);
            Promise.resolve(r).then(next); // 等待guard 执行完毕后自动调用next
        })
    }

    function extractGuards(guardType, matched, to, from) {
        let guards = []
        for (let record of matched) {
            let comp = record.components.default
            const guard = comp[guardType];
            guard && guards.push(guardToPromise(guard,to,from,record))
        }
        return guards
    }
    function runGuardQueue(guards) {
        return guards.reduce(
          (promise, guard) => promise.then(() => guard()),
          Promise.resolve()
        )
      }
    function navigate(to, from) {
        // to == from 更新
        // to(/my/a) => matched [/my,/my/a] from(/my)  [来]

        const [leavingRecords, updatingRecords, enteringRecords] = extractRecords(to, from);

        // 按照官方文档上的要求 抽离对应的组件的钩子 about里面有beforeRouteleave 
        
        let guards = extractGuards('beforeRouteLeave',leavingRecords.reverse(),to, from)
        return runGuardQueue(guards).then(() => {
            guards = []
            for (let guard of beforeGuards.list()) {
                guards.push(guardToPromise(guard))
            }
            return runGuardQueue(guards); // 全局钩子
        }).then(() => {
            // 更新
            let guards = extractGuards('beforeRouteUpdate', updatingRecords.reverse(), to, from);
            return runGuardQueue(guards); 
        }).then(() => {
            guards = []
            for (let record of to.matched) {
                const enterGuard = record.beforeEnter
                if (enterGuard) {
                    guards.push(guardToPromise(enterGuard))
                }
            }
            return  runGuardQueue(guards); 
        }).then(() => {
            guards = extractGuards('beforeRouteEnter',enteringRecords.reverse(),to, from)
            return  runGuardQueue(guards); 
        }).then(() => {
            guards = []
            for (let guard of beforeResolveGuards.list()) {
                guards.push(guardToPromise(guard))
            }
            return runGuardQueue(guards); // 全局钩子
        })
        // 这里我们需要将函数组合在一起来执行  compose
    }
    function pushWithRedirect(to) {
        const from = currentLocation.value; // 从哪来
        to = matcher.resolve(to.value || to);
        // 有 to 和from 我们就要监控 路径的变化，后续可以更新路径
        // 跳转路由 + 监听
        navigate(to, from).then(() => {
            return finalNavigation(to, from)
        }).then(() => {
            // 在特定的时间触发特定的钩子 来做处理
            for (let guard of afterGuards.list()) {
                guard(to,from)
            }
        })



       
    }
    function push(to) {
        return pushWithRedirect(to)
    }
    if (currentLocation.value === START_LOCATION) {
        // 这个时候是第一次加载路由，我们需要根据路径找到组件将他放入到START_LOCATION 中
        push(history.location); // 默认的跳转 
    }


    function useCallbacks() {
        const hanlders = []; // 用户的函数
        console.log(hanlders)
        const add = (handler) => hanlders.push(handler);
        return {
            add,
            list: () => hanlders
        }
    }


    const beforeGuards = useCallbacks();
    const beforeResolveGuards = useCallbacks();
    const afterGuards = useCallbacks();
    const router = {
        push, // $router.push('/')
        install(app) {
            // 核心注册两个组件
            app.provide('router', router); // 让router link可以拿到push方法进行跳转
            // console.log(currentLocation.value,reactive(currentLocation.value))

            let reactiveObj = {}
            for (let key in START_LOCATION) { // 将每个属性映射成计算属性  + reactive ==  reactive + toRefs
                reactiveObj[key] = computed(() => currentLocation.value[key]);
            }
            app.provide('location', reactive(reactiveObj)); // 根据这个路径和matched 匹配渲染

            app.component('RouterLink', RouterLink)
            app.component('RouterView', RouterView)

            // 让所有的子组件可以获取到路由   provide  globalProperties
        },
        beforeEach: beforeGuards.add,
        beforeResolve: beforeResolveGuards.add,
        afterEach:afterGuards.add
    }

    return router;
}

// redux 
// export default function compose(...funcs: Function[]) {
//     if (funcs.length === 0) {
//       // infer the argument type so it is usable in inference down the line
//       return <T>(arg: T) => arg
//     }
  
//     if (funcs.length === 1) {
//       return funcs[0]
//     }
  
//     return funcs.reduce(
//       (a, b) =>
//         (...args: any) =>
//           a(b(...args))
//     )
//   }

// const compose = require('koa-compose');




// promise -> Promise.resolve()
// guard  -> () => promise1
// [() => promise1, () => promise2]

// promise ->  Promise.resolve().then(() => (() => promise1)())
// guard ->  () => promise2

// Promise.resolve().then(() => (() => promise1)()).then(() => (() => promise2)())




 
// Promise.resolve().then(()=> (()=>promise)()).then(() => (() => promise)()),



// 1） vue路由中有两种方式  hash + h5Api -> h5Api = popstate + pushState 前端路由
// 2) 根据路径找到对应的匹配的组件 进行按照顺序来渲染，为了保证是响应式  shallowRef
// 3) 生命周期钩子 + 将钩子抽离出来 + 转换成promise  + 在组合运行
// 4) addRoute 动态路由添加


// 导航被触发。
// 在失活的组件里调用 beforeRouteLeave 守卫。
// 调用全局的 beforeEach 守卫。
// 在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
// 在路由配置里调用 beforeEnter。
// 解析异步路由组件。
// 在被激活的组件里调用 beforeRouteEnter。
// 调用全局的 beforeResolve 守卫(2.5+)。
// 导航被确认。
// 调用全局的 afterEach 钩子。
// 触发 DOM 更新。
// 调用 beforeRouteEnter 守卫中传给 next 的回调函数，创建好的组件实例会作为回调函数的参数传入。